// 版权所有2009 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package runtime

import (
	"internal/goarch"
	"runtime/internal/atomic"
	"unsafe"
)

// 编译器知道这种类型的值的打印应该使用printhex而不是printuint（十进制）。
type hex uint64

func bytes(s string) (ret []byte) {
	rp := (*slice)(unsafe.Pointer(&ret))
	sp := stringStructOf(&s)
	rp.array = sp.str
	rp.len = sp.len
	rp.cap = sp.len
	return
}

var (
	// printBacklog是一个循环缓冲区，由内置的
	// print*函数编写的消息组成，用于核心转储的事后分析。
	printBacklog      [512]byte
	printBacklogIndex int
)

// recordForPanic维护由
// 运行时写入的消息的循环缓冲区，导致进程崩溃，允许从核心转储中提取消息。
// 
// 进程崩溃期间（在“恐慌”或“致命
// 错误”之后）写入的文本不会保存，因为goroutine堆栈通常可以从核心文件的运行时数据结构中读取
// 。
func recordForPanic(b []byte) {
	printlock()

	if atomic.Load(&panicking) == 0 {
		// 未主动崩溃：维护打印输出的循环缓冲区。
		for i := 0; i < len(b); {
			n := copy(printBacklog[printBacklogIndex:], b[i:])
			i += n
			printBacklogIndex += n
			printBacklogIndex %= len(printBacklog)
		}
	}

	printunlock()
}

var debuglock mutex

// 编译器围绕
// 发出对printlock和printunlock的调用，这两个调用实现了一个Go print或println 
// 语句。一些打印帮助程序（例如printslice）
// 递归调用print。还有一个问题是在打印例程期间发生崩溃，需要获取打印锁来打印有关崩溃的信息。
// 出于这两个原因，让线程“递归”获取打印锁。

func printlock() {
	mp := getg().m
	mp.locks++ // 不要在printlock++和lock（&debuglock）之间重新调度。
	mp.printlock++
	if mp.printlock == 1 {
		lock(&debuglock)
	}
	mp.locks-- // 现在我们知道debuglock被占用了，占用了mp。给我们锁。
}

func printunlock() {
	mp := getg().m
	mp.printlock--
	if mp.printlock == 0 {
		unlock(&debuglock)
	}
}

// 如果转移输出，则写入goroutine本地缓冲区，
// 否则为标准错误。
func gwrite(b []byte) {
	if len(b) == 0 {
		return
	}
	recordForPanic(b)
	gp := getg()
	// 不要使用writebuf if gp。我快死了。如果我们处于恐慌状态，我们希望通过gwrite写入的任何内容都出现在终端中，而不是写入缓冲区中。
	// 请注意，我们不能在gp中清除writebuf。m、 濒死案例
	// 因为恐慌不允许有任何写入障碍。
	if gp == nil || gp.writebuf == nil || gp.m.dying > 0 {
		writeErr(b)
		return
	}

	n := copy(gp.writebuf[len(gp.writebuf):cap(gp.writebuf)], b)
	gp.writebuf = gp.writebuf[:len(gp.writebuf)+n]
}

func printsp() {
	printstring(" ")
}

func printnl() {
	printstring("\n")
}

func printbool(v bool) {
	if v {
		printstring("true")
	} else {
		printstring("false")
	}
}

func printfloat(v float64) {
	switch {
	case v != v:
		printstring("NaN")
		return
	case v+v == v && v > 0:
		printstring("+Inf")
		return
	case v+v == v && v < 0:
		printstring("-Inf")
		return
	}

	const n = 7 // 数字打印
	var buf [n + 7]byte
	buf[0] = '+'
	e := 0 // exp 
	if v == 0 {
		if 1/v < 0 {
			buf[0] = '-'
		}
	} else {
		if v < 0 {
			v = -v
			buf[0] = '-'
		}

		// 规范化
		for v >= 10 {
			e++
			v /= 10
		}
		for v < 1 {
			e--
			v *= 10
		}

		// 圆形
		h := 5.0
		for i := 0; i < n; i++ {
			h /= 10
		}
		v += h
		if v >= 10 {
			e++
			v /= 10
		}
	}

	// 格式+d.dddd+edd 
	for i := 0; i < n; i++ {
		s := int(v)
		buf[i+2] = byte(s + '0')
		v -= float64(s)
		v *= 10
	}
	buf[1] = buf[2]
	buf[2] = '.'

	buf[n+2] = 'e'
	buf[n+3] = '+'
	if e < 0 {
		e = -e
		buf[n+3] = '-'
	}

	buf[n+4] = byte(e/100) + '0'
	buf[n+5] = byte(e/10)%10 + '0'
	buf[n+6] = byte(e%10) + '0'
	gwrite(buf[:])
}

func printcomplex(c complex128) {
	print("(", real(c), imag(c), "i)")
}

func printuint(v uint64) {
	var buf [100]byte
	i := len(buf)
	for i--; i > 0; i-- {
		buf[i] = byte(v%10 + '0')
		if v < 10 {
			break
		}
		v /= 10
	}
	gwrite(buf[i:])
}

func printint(v int64) {
	if v < 0 {
		printstring("-")
		v = -v
	}
	printuint(uint64(v))
}

var minhexdigits = 0 // 受打印锁保护

func printhex(v uint64) {
	const dig = "0123456789abcdef"
	var buf [100]byte
	i := len(buf)
	for i--; i > 0; i-- {
		buf[i] = dig[v%16]
		if v < 16 && len(buf)-i >= minhexdigits {
			break
		}
		v /= 16
	}
	i--
	buf[i] = 'x'
	i--
	buf[i] = '0'
	gwrite(buf[i:])
}

func printpointer(p unsafe.Pointer) {
	printhex(uint64(uintptr(p)))
}
func printuintptr(p uintptr) {
	printhex(uint64(p))
}

func printstring(s string) {
	gwrite(bytes(s))
}

func printslice(s []byte) {
	sp := (*slice)(unsafe.Pointer(&s))
	print("[", len(s), "/", cap(s), "]")
	printpointer(sp.array)
}

func printeface(e eface) {
	print("(", e._type, ",", e.data, ")")
}

func printiface(i iface) {
	print("(", i.tab, ",", i.data, ")")
}

// hexdumpWords打印一个面向单词的十六进制转储文件[p，end）.
// 
// 如果mark！=nil，它将被调用，每个打印单词的地址都是
// 并且应该返回一个字符标记，显示在这个
// 单词的值之前。它可以返回0来表示没有标记。
func hexdumpWords(p, end uintptr, mark func(uintptr) byte) {
	printlock()
	var markbuf [1]byte
	markbuf[0] = ' '
	minhexdigits = int(unsafe.Sizeof(uintptr(0)) * 2)
	for i := uintptr(0); p+i < end; i += goarch.PtrSize {
		if i%16 == 0 {
			if i != 0 {
				println()
			}
			print(hex(p+i), ": ")
		}

		if mark != nil {
			markbuf[0] = mark(p + i)
			if markbuf[0] == 0 {
				markbuf[0] = ' '
			}
		}
		gwrite(markbuf[:])
		val := *(*uintptr)(unsafe.Pointer(p + i))
		print(hex(val))
		print(" ")

		// 我们可以象征val吗？
		fn := findfunc(val)
		if fn.valid() {
			print("<", funcname(fn), "+", hex(val-fn.entry()), "> ")
		}
	}
	minhexdigits = 0
	println()
	printunlock()
}
